namespace ScrumSprintMonitor.Design
{
	#region #using Directives

	using System;
	using System.Collections.Generic;
	using System.ComponentModel;
	using System.Linq;
	using System.Windows.Media;

	using Implementation;
	using Implementation.Configuration;

	using Infrastructure;
	using Infrastructure.Configuration;

	using Microsoft.Practices.ServiceLocation;

	using Rhino.Mocks;

	#endregion

	public sealed class TestAssetLibrary
	{
		public static ISprintConfiguration CreateSprintConfiguration()
		{
			var sprintConfiguration = MockRepository.GenerateStub<ISprintConfiguration>();

			sprintConfiguration.ProjectName = "Test Project";
			sprintConfiguration.ProcessTemplateName = "Conchango";
			sprintConfiguration.IterationPath = @"Release 1\Sprint 6";
			sprintConfiguration.BuildDefinitionNames = new[] {"Test CI Build 1", "Test CI Build 2"};
			sprintConfiguration.Stub(c => c.IsReadOnly).Return(true);
			sprintConfiguration.Stub(c => c.Save()).Throw(new NotImplementedException());
			sprintConfiguration.SprintStartTimeZone = TimeZoneInfo.Utc;
			sprintConfiguration.SprintStartDate = new DateTime(2009, 1, 21, 0, 0, 0);
			sprintConfiguration.SprintEndDate = new DateTime(2009, 2, 10, 0, 0, 0);
			sprintConfiguration.SprintEndTimeZone = TimeZoneInfo.Utc;

			return sprintConfiguration;
		}

		public static ITeamMemberInfoProvider CreateTeamMemberInfoProvider()
		{
			var unassignedFW = new UsTeamMemberInformation
								{
									Name = "<Unassigned>",
									Unassigned = true,
									Team = "FW",
									DedicatedToSprint = false
								};
			var matt = new UsTeamMemberInformation
						{
							Name = "Matt"
						};
			var roger = new UsTeamMemberInformation
							{
								Name = "Roger",
								DedicatedToSprint = false
							};
			var robert = new UsTeamMemberInformation
							{
								Name = "Robert"
							};
			var surya = new UsTeamMemberInformation
							{
								Name = "Surya"
							};
			var jesse = new UsTeamMemberInformation
							{
								Name = "Jesse"
							};
			var rolando = new UsTeamMemberInformation
							{
								Name = "Rolando"
							};
			var rohana = new UsTeamMemberInformation
							{
								Name = "Rohana"
							};
			var unassignedUX = new PtTeamMemberInformation
								{
									Name = "<Unassigned>",
									Unassigned = true,
									Team = "UX",
									DedicatedToSprint = false
								};
			var pedro = new PtTeamMemberInformation
							{
								Name = "Pedro"
							};
			var samuel = new BusyPtTeamMemberInformation
							{
								Name = "Samuel"
							};
			var teamInformation = new List<ITeamMemberInformation>
									{
										unassignedFW,
										matt,
										roger,
										robert,
										surya,
										jesse,
										rolando,
										rohana,
										unassignedUX,
										pedro,
										samuel,
									};
			return new TeamInformationProvider(teamInformation);
		}

		public static IConnection CreateTfsConnectionStub()
		{
			return CreateTfsConnectionStub(CreateSprintConfiguration());
		}

		public static IConnection CreateTfsConnectionStub(ISprintConfiguration sprintConfiguration)
		{
			var connection = MockRepository.GenerateStub<IConnection>();
			var buildInfo = MockRepository.GenerateStub<IBuildInfo>();

			buildInfo.Stub(b => b.Status).Return(BuildStatus.Succeeded);
			buildInfo.Stub(b => b.Number).Return("Project CI Build 2009.2.6.3");
			buildInfo.Stub(b => b.FinishTime).Return(SystemTime.Now().Date.AddHours(9));
			connection.Stub(c => c.GetBuildInfo(sprintConfiguration)).Return(buildInfo);
			connection.Stub(c => c.GetSprintName(sprintConfiguration)).Return("Sprint 4");
			connection.Stub(c => c.IsConnected).Return(true);

			return connection;
		}

		public static BindingList<IWorkItemAdapter> CreateWorkItems()
		{
			int id = 1;

			var list = new BindingList<IWorkItemAdapter>
						{
							CreateWorkItem(id++, "Pedro", "UX", WorkItemState.NotDone, 0.0, 4.0),
							CreateWorkItem(id++, "Pedro", "UX", WorkItemState.NotDone, 0.0, 16.0),
							CreateWorkItem(id++, "Pedro", "UX", WorkItemState.InProgress, 1.0, 3.0),
							CreateWorkItem(id++, "Pedro", "UX", WorkItemState.Done, 6.0, 0.0),
							CreateWorkItem(id++, "Roger", "FW", WorkItemState.NotDone, 0.0, 16.0),
						};

			return list;
		}

		public static BindingList<IWorkItemAdapter> CreateWorkItemsWithWorkNotEstimated()
		{
			var list = CreateWorkItems();

			list.Add(CreateWorkItemWithNoRemainingWorkEstimate(10, "Roger", "FW"));

			return list;
		}

		public static IWorkItemAdapter CreateWorkItem(int id, string assignedTo, string team, WorkItemState state, double doneWork, double remainingWork)
		{
			return CreateWorkItem(id, WorkItemType.SprintBacklogItem, assignedTo, team, state, doneWork, remainingWork);
		}

		public static IWorkItemAdapter CreateWorkItem(int id, string assignedTo, string team, double remainingWork)
		{
			return CreateWorkItem(id, WorkItemType.SprintBacklogItem, assignedTo, team, WorkItemState.NotDone, 0.0, remainingWork);
		}

		public static IWorkItemAdapter CreateWorkItem(int id, WorkItemType type, string assignedTo, string team, WorkItemState state, double doneWork,
													  double remainingWork)
		{
			var workItemAdapter = MockRepository.GenerateStub<IWorkItemAdapter>();

			workItemAdapter.Stub(wi => wi.Id).Return(id);
			workItemAdapter.Stub(wi => wi.Type).Return(type);
			workItemAdapter.Stub(wi => wi.AssignedTo).Return(assignedTo);
			workItemAdapter.Stub(wi => wi.Team).Return(team);
			workItemAdapter.Stub(wi => wi.DoneWork).Return(doneWork);
			workItemAdapter.Stub(wi => wi.RemainingWork).Return(remainingWork);
			workItemAdapter.Stub(wi => wi.State).Return(state);

			return workItemAdapter;
		}

		private static IWorkItemAdapter CreateWorkItemWithNoRemainingWorkEstimate(int id, string assignedTo, string team)
		{
			var workItemAdapter = MockRepository.GenerateStub<IWorkItemAdapter>();

			workItemAdapter.Stub(wi => wi.Id).Return(id);
			workItemAdapter.Stub(wi => wi.Type).Return(WorkItemType.SprintBacklogItem);
			workItemAdapter.Stub(wi => wi.AssignedTo).Return(assignedTo);
			workItemAdapter.Stub(wi => wi.Team).Return(team);
			workItemAdapter.Stub(wi => wi.DoneWork).Return(0.0);
			workItemAdapter.Stub(wi => wi.RemainingWork).Return(double.NaN);
			workItemAdapter.Stub(wi => wi.State).Return(WorkItemState.NotDone);

			return workItemAdapter;
		}

		public static IReportableData CreateReportableData()
		{
			return MockRepository.GenerateStub<IReportableData>();
		}

		public static IWorkItemStore CreateWorkItemStore(BindingList<IWorkItemAdapter> workItemAdapters)
		{
			var workItemStore = MockRepository.GenerateStub<IWorkItemStore>();

			workItemStore.Stub(s => s.WorkItems).Return(workItemAdapters);

			var refreshedEventRaiser = workItemStore.GetEventRaiser(x => x.WorkItemsRefreshed += null);
			var updatedEventRaiser = workItemStore.GetEventRaiser(x => x.WorkItemsUpdated += null);
			workItemStore.Stub(s => s.Refresh()).IgnoreArguments().Do((Action) (() =>
																				{
																					refreshedEventRaiser.Raise(workItemStore, new RefreshResultsEventArgs(null));
																					updatedEventRaiser.Raise(workItemStore, EventArgs.Empty);
																				}));

			return workItemStore;
		}

		public static void ConfigureSprintWithMockData(ISprint sprint, IServiceLocator serviceLocator)
		{
			var sprintConfiguration = serviceLocator.GetInstance<ISprintConfiguration>();
			var connection = CreateTfsConnectionStub(sprintConfiguration);

			sprint.Stub(s => s.AvailableTime).Return(TimeSpan.FromHours(80));
			sprint.Stub(s => s.TotalTime).Return(TimeSpan.FromHours(200));
			sprint.Stub(s => s.TimeIncludesFreeBusyInformation).Return(true);
			sprint.Stub(s => s.Start).Return(sprintConfiguration.SprintEndDate);
			sprint.Stub(s => s.End).Return(sprintConfiguration.SprintEndDate);
			sprint.Stub(s => s.LastRefresh).Return(SystemTime.Now());
			sprint.Stub(s => s.Name).Return(connection.GetSprintName(sprintConfiguration));
			sprint.Stub(s => s.Status).Return(SprintStatus.OK);
			var workItemStore = CreateWorkItemStore(CreateWorkItems());
			sprint.Stub(s => s.WorkItemStore).Return(workItemStore);
		}

		public static BindingList<ITeamMember> CreateTeamMembers(ISprintConfiguration sprintConfiguration, ITeamMemberInfoProvider teamMemberInfoProvider,
																 IEnumerable<IWorkItemAdapter> workItems)
		{
			var roger = new TeamMember(teamMemberInfoProvider.GetTeamMemberInformation("Roger"),
									   workItems.Where(x => x.Type == WorkItemType.SprintBacklogItem && x.AssignedTo == "Roger"),
									   new TeamMemberRemainingWorkload());
			var pedro = new TeamMember(teamMemberInfoProvider.GetTeamMemberInformation("Pedro"),
									   workItems.Where(x => x.Type == WorkItemType.SprintBacklogItem && x.AssignedTo == "Pedro"),
									   new TeamMemberRemainingWorkload());

			double workDone = 0.0;
			double workRemaining = 16.0;
			for (var at = sprintConfiguration.SprintStartDate; at <= SystemTime.Now() && at <= sprintConfiguration.SprintEndDate; at = at.AddDays(1.0))
			{
				var dataPoint = new BurndownDataPoint
									{
										Time = at,
										WorkDone = workDone,
										WorkRemaining = workRemaining
									};

				workDone += 1.0;
				workRemaining -= 1.0;

				pedro.BurndownDataPoints.Add(dataPoint);
			}

			var teamMembersList = new BindingList<ITeamMember>
									{
										roger,
										pedro
									};

			return teamMembersList;
		}

		#region Nested type: HardcodedPresentationConfiguration

		public class HardcodedPresentationConfiguration : IPresentationConfiguration
		{
			private readonly RemainingWorkStatusPresentation[] ranges;

			public HardcodedPresentationConfiguration()
			{
				var defaultRanges = new List<RemainingWorkStatusPresentation>();
				AddDefaultRange(RemainingWorkStatus.NoWorkLeft, Colors.Transparent, Colors.White, 0.0, defaultRanges);
				AddDefaultRange(RemainingWorkStatus.SeverelyUnderassigned, Colors.MidnightBlue, Colors.White, 0.5, defaultRanges);
				AddDefaultRange(RemainingWorkStatus.Underassigned, Color.FromRgb(0, 0, 200), Colors.White, 0.8, defaultRanges);
				AddDefaultRange(RemainingWorkStatus.Normal, Color.FromRgb(0, 200, 0), Colors.White, 1.0, defaultRanges);
				AddDefaultRange(RemainingWorkStatus.Warning, Colors.Orange, Colors.White, 1.05, defaultRanges);
				AddDefaultRange(RemainingWorkStatus.Overassigned, Color.FromRgb(225, 0, 0), Colors.White, 1.20, defaultRanges);
				AddDefaultRange(RemainingWorkStatus.SeverelyOverassigned, Color.FromRgb(200, 0, 0), Colors.White, double.PositiveInfinity, defaultRanges);

				this.ranges = defaultRanges.ToArray();
			}

			private static void AddDefaultRange(RemainingWorkStatus status, Color backgroundColor, Color foregroundColor, double upperLimit,
												ICollection<RemainingWorkStatusPresentation> defaultRanges)
			{
				defaultRanges.Add(new RemainingWorkStatusPresentation(status, defaultRanges.LastOrDefault())
									{
										BackgroundColor = backgroundColor,
										ForegroundColor = foregroundColor,
										UpperLimit = upperLimit
									});
			}

			#region Implementation of IPresentationConfiguration

			public IRemainingWorkStatusPresentationConfig GetRemainingWorkStatusPresentationConfig(RemainingWorkStatus status)
			{
				int index = (int) status;
				if (index < 0 || index >= this.ranges.Length)
				{
					throw new ArgumentException("status");
				}

				return this.ranges[index];
			}

			public bool IsReadOnly
			{
				get { return true; }
			}

			public void Save()
			{
				throw new NotImplementedException();
			}

			#endregion
		}

		#endregion

		#region Nested type: HardcodedRemainingWorkStatusEvaluator

		public class HardcodedRemainingWorkStatusEvaluator : IRemainingWorkStatusEvaluator
		{
			#region IRemainingWorkStatusEvaluator Members

			public RemainingWorkStatus Evaluate(double remainingWork, double availableTime)
			{
				double ratio = remainingWork / availableTime;

				if (ratio >= 1.20)
				{
					return RemainingWorkStatus.SeverelyOverassigned;
				}
				if (ratio >= 1.05)
				{
					return RemainingWorkStatus.Overassigned;
				}
				if (ratio > 1.0)
				{
					return RemainingWorkStatus.Warning;
				}
				if (ratio >= 0.8)
				{
					return RemainingWorkStatus.Normal;
				}
				if (ratio >= 0.5)
				{
					return RemainingWorkStatus.Underassigned;
				}
				if (double.IsNaN(ratio))
				{
					return RemainingWorkStatus.NoWorkLeft;
				}

				return RemainingWorkStatus.SeverelyUnderassigned;
			}

			#endregion
		}

		#endregion

		#region Nested type: PtTeamMemberInformation

		private class PtTeamMemberInformation : ITeamMemberInformation
		{
			public PtTeamMemberInformation()
			{
				DedicatedToSprint = true;
				Unassigned = false;
				Team = "UX";
			}

			#region Implementation of ITeamMemberInformation

			public string Name { get; internal set; }

			public bool Unassigned { get; set; }

			public string DisplayAlias { get; set; }

			public IAvatar Avatar
			{
				get { return null; }
				set { throw new NotImplementedException(); }
			}

			public string Team { get; set; }

			public string SchedulerAlias
			{
				get { return null; }
			}

			public string DomainName { get; set; }

			public TimeZoneInfo TimeZone
			{
				get { return TimeZoneInfo.Utc; }
			}

			public double HourlyProductivityFactor
			{
				get { return 1.0; }
			}

			public bool DedicatedToSprint { get; set; }

			public DateTime GetWorkdayStart(DateTime date)
			{
				var startTime = date.Date.AddHours(9);

				return startTime;
			}

			public DateTime GetWorkdayEnd(DateTime date)
			{
				var endTime = date.Date.AddHours(18);

				return endTime;
			}

			public virtual bool IsBusy(DateTime time, TimeSpan slotSize)
			{
				return (time.Hour >= 12 && time.Hour < 13);
			}

			#endregion

			#region Implementation of INotifyPropertyChanged

#pragma warning disable 0067
			public event PropertyChangedEventHandler PropertyChanged;
#pragma warning restore 0067

			#endregion
		}

		#endregion

		#region Nested type: BusyPtTeamMemberInformation

		private class BusyPtTeamMemberInformation : PtTeamMemberInformation
		{
			#region Implementation of ITeamMemberInformation

			public override bool IsBusy(DateTime time, TimeSpan slotSize)
			{
				return true;
			}

			#endregion
		}

		#endregion

		#region Nested type: TeamInformationProvider

		public class TeamInformationProvider : ITeamMemberInfoProvider
		{
			private readonly List<ITeamMemberInformation> list;

			public TeamInformationProvider(List<ITeamMemberInformation> list)
			{
				this.list = list;
			}

			#region Implementation of ITeamMemberInfoProvider

			public ITeamMemberInformation GetTeamMemberInformation(string teamMemberName)
			{
				if (string.IsNullOrEmpty(teamMemberName))
				{
					throw new ArgumentNullException("teamMemberName");
				}
				if (teamMemberName.StartsWith("unassigned:"))
				{
					return this.list.Find(x => x.Unassigned && x.Team == teamMemberName.Substring(11));
				}

				return this.list.Find(x => x.Name == teamMemberName);
			}

			public bool IsReadOnly
			{
				get { return true; }
			}

			public void Save()
			{
				throw new NotImplementedException();
			}

			public IEnumerable<ITeamMemberInformation> TeamMembers
			{
				get { return this.list; }
			}

			#endregion

			#region INotifyPropertyChanged members

			public event PropertyChangedEventHandler PropertyChanged;

			public void OnPropertyChanged(string propertyName)
			{
				if (PropertyChanged != null)
				{
					PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
				}
			}

			#endregion
		}

		#endregion

		#region Nested type: UsTeamMemberInformation

		public class UsTeamMemberInformation : ITeamMemberInformation
		{
			public UsTeamMemberInformation()
			{
				DedicatedToSprint = true;
				Unassigned = false;
				Team = "FW";
			}

			#region Implementation of ITeamMemberInformation

			public string Name { get; set; }

			public bool Unassigned { get; set; }

			public string DisplayAlias { get; set; }
			public IAvatar Avatar { get; set; }

			public string Team { get; set; }

			public string SchedulerAlias
			{
				get { return null; }
			}

			/// <summary>
			///   The domain name of the team member (e.g. company\username).
			/// </summary>
			public string DomainName { get; set; }

			public TimeZoneInfo TimeZone
			{
				get { return TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time"); }
			}

			public double HourlyProductivityFactor
			{
				get { return 1.0; }
			}

			public bool DedicatedToSprint { get; set; }

			public DateTime GetWorkdayStart(DateTime date)
			{
				var startTime = date.Date.AddHours(8);

				return startTime;
			}

			public DateTime GetWorkdayEnd(DateTime date)
			{
				var endTime = date.Date.AddHours(17);

				return endTime;
			}

			public bool IsBusy(DateTime time, TimeSpan slotSize)
			{
				return (time.Hour >= 12 && time.Hour < 13);
			}

			#endregion

			#region Implementation of INotifyPropertyChanged

#pragma warning disable 0067
			public event PropertyChangedEventHandler PropertyChanged;
#pragma warning restore 0067

			#endregion
		}

		#endregion
	}
}